package com.exam.api.modules.system.service.impl;

import cn.dev33.satoken.stp.StpInterface;
import cn.dev33.satoken.stp.StpUtil;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.ObjUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.exam.api.common.Const;
import com.exam.api.common.exception.GlobalException;
import com.exam.api.common.utils.LambdaQueryUtil;
import com.exam.api.common.web.ResultCode;
import com.exam.api.modules.system.domain.TblDept;
import com.exam.api.modules.system.domain.TblUser;
import com.exam.api.modules.system.dto.*;
import com.exam.api.modules.system.mapper.TblUserMapper;
import com.exam.api.modules.system.service.*;
import com.exam.api.modules.system.vo.BasicUserInfo;
import com.exam.api.modules.system.vo.LoginUserInfo;
import com.exam.api.modules.system.vo.UserRoleVO;
import com.exam.api.modules.system.vo.UserVO;
import jakarta.annotation.Resource;
import org.redisson.api.RBucket;
import org.redisson.api.RedissonClient;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.io.Serializable;
import java.time.Duration;
import java.util.*;
import java.util.stream.Collectors;

/**
 * 用户逻辑接口类实现类
 *
 * @author bcheng
 */
@Service
public class TblUserServiceImpl extends ServiceImpl<TblUserMapper, TblUser> implements TblUserService, StpInterface {

    @Resource
    private TblRoleService tblRoleService;
    @Resource
    private TblMenuService tblMenuService;
    @Resource
    private TblUserRoleService tblUserRoleService;
    @Resource
    private TblDeptService tblDeptService;
    @Resource
    private RedissonClient redissonClient;

    @Override
    public void updatePassword(UpdatePasswordDTO dto) {
        long userId = StpUtil.getLoginIdAsLong();
        Optional<TblUser> userOpt = lambdaQuery()
                .eq(TblUser::getUserId, userId)
                .eq(TblUser::getIsDelete, Const.ZERO)
                .oneOpt();
        if (userOpt.isEmpty()) {
            StpUtil.logout();
            StpUtil.checkLogin();
            return;
        }
        TblUser user = userOpt.get();
        if (!StrUtil.equals(user.getPassword(), dto.getOldPassword())) {
            throw new GlobalException("旧密码不正确");
        }
        user.setPassword(dto.getNewPassword());
        boolean updated = lambdaUpdate()
                .set(TblUser::getPassword, dto.getNewPassword())
                .eq(TblUser::getUserId, userId)
                .update();
        if (!updated) {
            throw new GlobalException("密码修改失败");
        }
        StpUtil.logout();
    }

    @Override
    public LoginUserInfo getLoginUser() {
        Long userId = StpUtil.getLoginIdAsLong();
        RBucket<LoginUserInfo> bucket = redissonClient.getBucket("LoginUserHitCacheBucket:" + userId);
        return Optional.ofNullable(bucket.get()).orElseGet(() -> {
            if (bucket.isExists()) {
                return bucket.get();
            }
            TblUser user = getById(userId);
            user.setPassword(null);
            LoginUserInfo loginUser = new LoginUserInfo();
            BeanUtil.copyProperties(user, loginUser);
            if (ObjUtil.isNotEmpty(user.getDeptId())) {
                // 获取部门名称
                tblDeptService.lambdaQuery()
                        .select(TblDept::getName, TblDept::getParentDeptId)
                        .eq(TblDept::getDeptId, user.getDeptId())
                        .oneOpt()
                        .ifPresent(dept -> {
                            loginUser.setDeptName(dept.getName());
                            loginUser.setParentDeptId(dept.getParentDeptId());
                        });
            }
            Map<Long, List<UserRoleVO>> mapUserRoleList = tblUserRoleService.mapUserRoleList(List.of(userId));
            loginUser.setUserRoles(mapUserRoleList.getOrDefault(userId, Collections.emptyList()));
            bucket.set(loginUser, Duration.ofSeconds(10));
            return loginUser;
        });
    }


    @Override
    public Optional<TblUser> getUserByUsername(String tel) {
        Optional<TblUser> userOpt = lambdaQuery()
                .eq(TblUser::getTel, tel)
                .or()
                .eq(TblUser::getJobNo, tel)
                .ne(TblUser::getStatus, Const.TWO)
                .oneOpt();
        if (userOpt.isPresent()) {
            TblUser user = userOpt.get();
            if (Const.ONE.equals(user.getStatus())) {
                throw new GlobalException(ResultCode.E1001);
            }
        }
        return userOpt;
    }

    @Override
    public IPage<UserVO> pageUser(UserPageDTO dto) {
        Page<UserVO> page = baseMapper.pageUser(Page.of(dto.getPageNum(), dto.getPageSize()), dto);
        Set<Long> userIds = page.getRecords().stream()
                .map(UserVO::getUserId)
                .collect(Collectors.toSet());
        Map<Long, List<UserRoleVO>> mapUserRoleList = tblUserRoleService.mapUserRoleList(userIds);
        return page.convert(userVO -> {
            userVO.setUserRoles(mapUserRoleList.getOrDefault(userVO.getUserId(), Collections.emptyList()));
            return userVO;
        });
    }

    @Override
    public LoginUserInfo getUser(UserIdDTO dto) {
        TblUser user = getById(dto.getUserId());
        if (user == null) {
            throw new GlobalException(ResultCode.E1000);
        }
        user.setPassword(null);
        LoginUserInfo loginUser = new LoginUserInfo();
        BeanUtil.copyProperties(user, loginUser);
        if (ObjUtil.isNotEmpty(user.getDeptId())) {
            // 获取部门名称
            tblDeptService.lambdaQuery()
                    .select(TblDept::getName)
                    .eq(TblDept::getDeptId, user.getDeptId())
                    .oneOpt()
                    .ifPresent(dept -> loginUser.setDeptName(dept.getName()));
        }

        List<Long> strings = tblRoleService.listRoleIdsByUserId(dto.getUserId());
        loginUser.setRoleIds(strings);
        return loginUser;
    }

    @Override
    public void addUser(UserAddDTO dto) {
        Long telCount = lambdaQuery()
                .eq(TblUser::getTel, dto.getTel())
                .count();
        if (telCount > 0) {
            throw new GlobalException("账号重复，请换一个再试");
        }
        Long jobNoCount = lambdaQuery()
                .eq(TblUser::getJobNo, dto.getJobNo())
                .count();
        if (jobNoCount > 0) {
            throw new GlobalException("工号重复，请换一个再试");
        }
        TblUser role = dto.converter();
        role.setPassword("123456");
        save(role);
        tblUserRoleService.saveUserRole(role.getUserId(), dto.getRoleIds());
    }

    @Override
    public void updateUser(UserUpdateDTO dto) {
        Long telCount = lambdaQuery()
                .eq(TblUser::getTel, dto.getTel())
                .ne(TblUser::getUserId, dto.getUserId())
                .count();
        if (telCount > 0) {
            throw new GlobalException("账号重复，请换一个再试");
        }
        Long jobNoCount = lambdaQuery()
                .eq(TblUser::getJobNo, dto.getJobNo())
                .ne(TblUser::getUserId, dto.getUserId())
                .count();
        if (jobNoCount > 0) {
            throw new GlobalException("工号重复，请换一个再试");
        }
        updateById(dto.converter());
        tblUserRoleService.saveUserRole(dto.getUserId(), dto.getRoleIds());
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void deleteUser(UserIdDTO dto) {
        removeById(dto.getUserId());
    }

    @Override
    public void restPassword(UserIdDTO dto) {
        boolean exists = lambdaQuery()
                .eq(TblUser::getUserId, dto.getUserId())
                .exists();
        if (!exists) {
            throw new GlobalException("用户不存在");
        }
        boolean update = lambdaUpdate()
                .set(TblUser::getPassword, "123456")
                .eq(TblUser::getUserId, dto.getUserId())
                .update();
        if (!update) {
            throw new GlobalException("重置密码失败");
        }
    }

    @Override
    public List<String> getRoleList(Object loginId, String loginType) {
        long userId = Long.parseLong(loginId.toString());
        return tblRoleService.listRoleCodes(userId);
    }

    @Override
    public List<String> getPermissionList(Object loginId, String loginType) {
//        long userId = Long.parseLong(loginId.toString());
//        return tblMenuService.listPermissionCodes(userId); // TODO
        List<String> list = new ArrayList<>();
        list.add("*");
        return list;
    }

    @Override
    public Map<Long, String> getNickNameMap(Collection<Long> ids) {
        if (CollUtil.isEmpty(ids)) {
            return Collections.emptyMap();
        }
        List<TblUser> list = lambdaQuery()
                .select(TblUser::getUserId, TblUser::getNickName)
                .in(TblUser::getUserId, ids)
                .list();
        if (CollUtil.isEmpty(list)) {
            return Collections.emptyMap();
        }
        return list.stream().collect(Collectors.toMap(TblUser::getUserId, TblUser::getNickName));
    }

    @Override
    public List<Long> listUserIdByNickName(String name) {
        List<TblUser> list = lambdaQuery()
                .like(TblUser::getNickName, name)
                .or()
                .like(TblUser::getRealName, name)
                .select(TblUser::getUserId)
                .list();
        if (CollUtil.isEmpty(list)) {
            return List.of();
        }
        return list.stream().map(TblUser::getUserId).collect(Collectors.toList());
    }

    @Override
    public List<Long> listUserIdByDeptIds(Collection<Long> deptIds) {
        List<TblUser> list = lambdaQuery()
                .in(TblUser::getDeptId, deptIds)
                .select(TblUser::getUserId)
                .list();
        if (CollUtil.isEmpty(list)) {
            return List.of();
        }
        return list.stream().map(TblUser::getUserId).collect(Collectors.toList());
    }

    @Override
    public String getNickNameById(Serializable userId) {
        String nickName = lambdaQuery()
                .eq(TblUser::getUserId, userId)
                .select(TblUser::getNickName)
                .oneOpt().orElseGet(() -> {
                    TblUser user = new TblUser();
                    user.setNickName("未知");
                    return user;
                }).getNickName();
        return nickName;
    }

    @Override
    public List<BasicUserInfo> listBasicUserInfos(UserBasicQueryDTO dto) {
        List<TblUser> list = lambdaQuery()
                .in(CollUtil.isNotEmpty(dto.getUserIds()), TblUser::getUserId, dto.getUserIds())
                .in(CollUtil.isNotEmpty(dto.getDeptIds()), TblUser::getDeptId, dto.getDeptIds())
                .func(StrUtil.isNotEmpty(dto.getKeyword()),
                        wrapper -> wrapper
                                .like(TblUser::getJobNo, dto.getKeyword())
                                .or()
                                .like(TblUser::getTel, dto.getKeyword())
                                .or()
                                .like(TblUser::getNickName, dto.getKeyword()))
                .orderByDesc(TblUser::getCreateTime, TblUser::getUserId)
                .select(field -> LambdaQueryUtil.select(dto.getQueryFields(), field,
                        TblUser::getUserId,
                        TblUser::getDeptId,
                        TblUser::getNickName,
                        TblUser::getTel,
                        TblUser::getJobNo)
                ).list();
        return BeanUtil.copyToList(list, BasicUserInfo.class);
    }
}
